using System;
using System.Collections.Generic;
using Module = winS.Unity.Module;

namespace winS.UnityEditor
{
    internal class EditorModuleManager
    {
        private readonly List<Type> moduleTypeList = new List<Type>();

        private readonly List<Module> moduleList = new List<Module>();
        private readonly Dictionary<Type, int> moduleCacheDictionary = new Dictionary<Type, int>();

        public T GetModule<T>() where T : Module
        {
            Type requestModuleType = typeof(T);
            //1.Find from Cache
            if (moduleCacheDictionary.TryGetValue(requestModuleType, out int index)) return moduleList[index] as T;
            //2.Find from Type
            if (TryFindFromType(out Module module)) return module as T;
            //3.Scan and do Step2 Again
            ScanAllModuleTypes();
            TryFindFromType(out module);
            return module as T;

            bool TryFindFromType(out Module module)
            {
                int foundIndex = moduleTypeList.FindIndex(moduleType => requestModuleType.IsAssignableFrom(moduleType));
                if (foundIndex == -1)
                {
                    module = default;
                    return false;
                }
                module = Activator.CreateInstance(moduleTypeList[foundIndex]) as T;
                moduleList.Add(module);
                moduleCacheDictionary.Add(requestModuleType, moduleList.Count - 1);
                moduleTypeList.RemoveAt(foundIndex);
                return true;
            }
        }

        private void ScanAllModuleTypes()
        {
            Type baseType = typeof(Module);
            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                foreach (var type in assembly.GetTypes())
                {
                    if (type.IsAbstract || !type.IsSubclassOf(baseType)) continue;
                    //no parameterless constructor, Error!
                    if (type.GetConstructor(Type.EmptyTypes) == null) throw new Exception($"{nameof(Editor)}: Module \"{type.Name}\" must have parameterless constructor!");
                    moduleTypeList.Add(type);
                }
            }
        }
    }
}